#include "virtio_disk_impl.h"
#include <stdlib.h>
//max_size => int 

#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>


#define MAX_FILE_NAME_LEN   128

struct virtio_disk_impl {
  char fname[MAX_FILE_NAME_LEN];
  int fd;
  unsigned long size;
};


void virtio_disk_impl_destroy(virtio_disk_impl_t * impl) {
  if(impl != NULL){
    if(impl->fd >= 0){
      close(impl->fd);
      impl->fd = 0;
    }
    impl->size = 0;
    impl->fname[0] = '\0';
    free(impl);
  }
}

virtio_disk_impl_t *  virtio_disk_impl_create(char * fname, unsigned long size) {
  virtio_disk_impl_t * impl = (virtio_disk_impl_t *)malloc(sizeof(virtio_disk_impl_t));
  assert(impl != NULL);

  strncpy(impl->fname, fname, MAX_FILE_NAME_LEN);

  impl->fd = open(impl->fname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
  if(impl->fd == -1) {
    printf("ERR: open file:[%s] failed, errno:[%d]\n", impl->fname,  errno);
    virtio_disk_impl_destroy(impl);
    return NULL;
  }

  struct stat stat;
  if(fstat(impl->fd, &stat) == -1){
    printf("ERR: fstat file:[%s]-fd[%d] failed, errno:[%d]\n", impl->fname, impl->fd, errno);
    virtio_disk_impl_destroy(impl);
    return NULL;
  }

  if(size % SECTOR != 0){
    printf("ERR: size must be align to sector size:[%d]\n", SECTOR);
    virtio_disk_impl_destroy(impl);
    return NULL;
  }
  
  if(stat.st_size <= size) {
    if(ftruncate(impl->fd, size) == -1){
      printf("ERR: truncate file to size:[%lu] failed\n", size);
    }
  }
  impl->size = size;
  return impl;
}

void virtio_disk_impl_print(virtio_disk_impl_t * impl) {
  if(impl != NULL){
    printf("virtio_disk_impl: fname:[%s], fd:[%d], sectors:[%lu], size:[%lu]\n", impl->fname, impl->fd, impl->size/SECTOR, impl->size);
  } else{
    printf("virtio_disk_impl: is NULL\n");
  }
}


int virtio_disk_impl_seek(virtio_disk_impl_t * impl, unsigned long offset){
  if(offset % SECTOR != 0){
    printf("ERR:seek offset:[%ld] must be align to SECTOR(512)\n", offset);
    return -1;
  }
  off_t ret = lseek(impl->fd, offset, SEEK_SET);
  if(ret == -1){
    printf("ERR: seek fname:[%s], fd[%d] failed!\n", impl->fname, impl->fd);
  }
  return (int)ret;
}

int virtio_disk_impl_rw(virtio_disk_impl_t * impl, unsigned char * data, int size, int is_write){
  if(size % SECTOR != 0){
    printf("ERR:read(0)/write(1):[%d] 's size:[%d] must be align to SECTOR(512)\n", is_write, size);
    return -1;
  }
  ssize_t bytes = (is_write == 1) ? write(impl->fd, data, size)
    : read(impl->fd, data, size);
  if(bytes == -1){
    printf("ERR: read(0)/write(1):[%d] from fname:[%s], fd[%d] failed!\n", is_write, impl->fname, impl->fd);
  }
  return (int)bytes;
}

void virtio_disk_impl_zero(virtio_disk_impl_t * impl){
  virtio_disk_impl_seek(impl, 0);

  char * buf = (char*)calloc(1024, 1);
  for(int i = 0; i < (impl->size)/1024; ++i){
    virtio_disk_impl_rw(impl, buf, 1024, 1);
  }
  free(buf);
}
